/*****************************************************************************
* @brief   nand lut .h
*****************************************************************************/
#ifndef _NAND_LUT_H
#define _NAND_LUT_H

#include "common.h"

typedef struct nand_lut_str nand_lut_type;
/*****************************************************************************
* @brief   DBG
*****************************************************************************/
#define LUT_DBG_EN 1
#if LUT_DBG_EN > 0
#define lut_print dbg_print
#define lut_puts dbg_puts
#else
#define lut_print(...)
#define lut_puts(...)
#endif

/*****************************************************************************
* @brief   LOGIG NULL
*****************************************************************************/
#define LUT_LOGIC_NULL 0xFFFFU

/*****************************************************************************
* @brief   PAGE SPARE FUNCTION
*****************************************************************************/
#define LUT_PAGE_OFFSET_INFO 0
#define LUT_PAGE_OFFSET_RTC 2	//rtc所在页
#define LUT_PAGE_OFFSET_RTM 3	//设备运行总时间所在页
#define LUT_PAGE_OFFSET_BATCH 4 //存储批次

/*****************************************************************************
* @brief   NAND HAL
*****************************************************************************/
typedef struct
{
	uint8_t (*ecc_error_get)(void); //1-ecc error, 0-ecc not error
	uint8_t (*read_page)(uint32_t page_addr);
	uint8_t (*read_cache)(uint16_t page_addr, uint8_t *dat, uint16_t Len);
	uint8_t (*read_spare)(uint32_t page_addr, uint8_t *dat, uint16_t Len); // 仅用于创建LUT表和读坏块标记
	uint8_t (*write_cache)(uint16_t page_addr, const uint8_t *dat, uint16_t Len);
	uint8_t (*program_exe)(uint32_t page_addr);
	uint8_t (*block_erase)(uint32_t block_addr);
	uint8_t (*init)(void); // 可选不需要时，设为0即可
} nand_lut_hal_type;

/*****************************************************************************
* @brief   NAND api
*****************************************************************************/
typedef struct
{
	/*-打开lut-*/
	void (*open)(char (*dev_link)(nand_lut_type *lut));
	void (*close)(void);
	/*-创建lut-*/
	void (*lut_creat)(void);
	/*-块擦除-*/
	uint8_t (*block_erase)(uint16_t logic_block);
	/*-块写，内含坏块管理-返回1代表错误中断-*/
	uint8_t (*block_write)(uint16_t logic_block, uint8_t (*write_dat_get)(nand_lut_type *lut, uint32_t addr_offset, uint8_t *pdat, uint16_t len));
	/*-页读-整页-*/
	uint8_t (*page_read)(uint32_t logic_page_start, uint8_t *pdat, uint16_t page_len);
	/*-任意读-*/
	uint8_t (*inp_read)(uint32_t logic_page, uint16_t cell_offset, int32_t dat_len, uint8_t *pdat);
	/*-读，地址转换-*/
	uint8_t (*addr_read)(uint32_t addr, int32_t dat_len, uint8_t *pdat);
	/*-读，初始化-*/
	uint8_t (*read_init)(void);
	/*-实时更管理-*/
	uint8_t (*update_manager)(void);
	/*-更新TIME-*/
	void (*time_update)(uint32_t rt_minute);
	/*-更新一次-*/
	void (*update_once)(void);
} nand_lut_api_type;

/*****************************************************************************
* @brief   UPDATE MANAGER
*****************************************************************************/
typedef struct
{
	uint8_t b_new_req : 1;	 // 更新到新块请求
	uint8_t b_scan_comp : 1; // 值扫描标记
	uint8_t b_scan_req : 1;	 //扫描请求

	uint8_t minute_old;		  // 计时用
	uint16_t scan_blk;		  // RTC扫描计数
	uint16_t old_phy_block;	  // 当前更新逻辑块对应的旧物理块
	uint16_t minute_cnt;	  // 更新后运行分钟数
	uint16_t logic_batch_min; // 搜到的目标逻辑块
	uint32_t batch_min;		  // 批次最小值
	uint32_t batch_max;		  // 批次最大值
	uint32_t batch_now;		  // 当前批次
} nand_lut_update_type;

/*****************************************************************************
* @brief   NAND属性结构体
*****************************************************************************/
struct nand_lut_str
{
	// 外部实时更新-参考时间分钟数
	uint32_t ref_minute; //设备运行总时间-分钟

	// 外部配置
	uint16_t page_size;			 //-每页总大小，main区和spare区总和
	uint16_t page_main_size;	 //-每页的main区大小
	uint16_t page_spare_size;	 //-每页的spare区大小
	uint16_t block_per_plane;	 //-每个plane包含的块数量
	uint16_t block_total;		 //-总的块数量
	uint16_t logic_block_amount; //-逻辑块数
	uint16_t update_period;		 //-更新周期-分钟
	uint8_t page_per_block;		 //-每个块包含的页数量
	uint8_t keep_err_logic;		 //-是否保持逻辑号错误的块
	// 内部管理
	uint16_t valid_block;	  //-有效块数量(已分配逻辑号)
	uint16_t bad_block;		  //-坏块
	uint16_t illegal_block;	  //-非法块,逻辑错误，spare校验错误
	uint16_t empty_block;	  //-空块
	uint16_t new_block_next;  //-下一个新块起始搜索位置
	uint32_t page_read_old;	  //-最近一次读取的物理页
	uint32_t wr_blk_used_cnt; //-当前写入逻辑块使用次数计数
	uint16_t *tab;			  //-LUT表，用作逻辑块-物理块转换
	uint8_t *buff;			  //-缓存

	// 更新管理
	nand_lut_update_type update;

	// 硬件接口
	nand_lut_hal_type hal;

	// 应用接口
	nand_lut_api_type api;
};

/*****************************************************************************
* @brief   备份区数据结构
*****************************************************************************/
typedef union
{
	struct
	{
		uint16_t bad_mark;
		uint16_t lrc;
		uint16_t logic;
		uint16_t used_cnt;
		uint16_t ver;
	} info;

	struct
	{
		uint16_t bad_mark;
		uint16_t lrc;
		uint32_t batch; //存储批次
	} batch;

	struct
	{
		uint16_t bad_mark;
		uint16_t lrc;
		uint32_t minute; //设备运行总时间
	} rtm;
} lut_spare_type;

/*****************************************************************************
* @brief   API
*****************************************************************************/
/*-初始化-*/
void nand_lut_open(nand_lut_type *lut, char (*dev_link)(nand_lut_type *lut));
void nand_lut_close(nand_lut_type *lut);

/*-创建lut-*/
void nand_lut_creat(nand_lut_type *lut);

/*-块擦除-*/
uint8_t nand_lut_block_erase(nand_lut_type *lut, uint16_t logic_block);

/*-块写，内含坏块管理*/
uint8_t nand_lut_block_write(nand_lut_type *lut, uint16_t logic_block, uint8_t (*write_dat_get)(nand_lut_type *lut, uint32_t addr_offset, uint8_t *pdat, uint16_t len)); //-返回1代表错误中断

/*-页读-整页-*/
uint8_t nand_lut_page_read(nand_lut_type *lut, uint32_t logic_page_start, uint8_t *pdat, uint16_t page_len);

/*-任意读-*/
uint8_t nand_lut_inp_read(nand_lut_type *lut, uint32_t logic_page, uint16_t cell_offset, int32_t dat_len, uint8_t *pdat);

/*-读，地址转换-*/
uint8_t nand_lut_addr_read(nand_lut_type *lut, uint32_t addr, int32_t dat_len, uint8_t *pdat);

/*-读，初始化-*/
uint8_t nand_lut_read_init(nand_lut_type *lut);

/*-实时更管理-搜索最小信息块-控制更新-外部给new_req更新一块*/
uint8_t nand_lut_update_manager(nand_lut_type *lut);

/*****************************************************************************
* @brief   API
*****************************************************************************/
#define nand_lut_api_make(obj)                                                    \
	void obj##_open(char (*dev_link)(nand_lut_type * lut))                        \
	{                                                                             \
		nand_lut_open(&obj, dev_link);                                            \
	}                                                                             \
	void obj##_close(void) { nand_lut_close(&obj); }                              \
	void obj##_lut_creat(void) { nand_lut_creat(&obj); }                          \
	uint8_t obj##_block_erase(uint16_t logic_block)                               \
	{                                                                             \
		return nand_lut_block_erase(&obj, logic_block);                           \
	}                                                                             \
	uint8_t obj##_block_write(                                                    \
		uint16_t logic_block,                                                     \
		uint8_t (*write_dat_get)(nand_lut_type *, uint32_t, uint8_t *, uint16_t)) \
	{                                                                             \
		return nand_lut_block_write(&obj, logic_block, write_dat_get);            \
	}                                                                             \
	uint8_t obj##_page_read(uint32_t logic_page_start,                            \
							uint8_t *pdat, uint16_t page_len)                     \
	{                                                                             \
		return nand_lut_page_read(&obj, logic_page_start, pdat, page_len);        \
	}                                                                             \
	uint8_t obj##_inp_read(uint32_t logic_page, uint16_t cell_offset,             \
						   int32_t dat_len, uint8_t *pdat)                        \
	{                                                                             \
		return nand_lut_inp_read(&obj, logic_page, cell_offset, dat_len, pdat);   \
	}                                                                             \
	uint8_t obj##_addr_read(uint32_t addr, int32_t dat_len, uint8_t *pdat)        \
	{                                                                             \
		return nand_lut_addr_read(&obj, addr, dat_len, pdat);                     \
	}                                                                             \
	uint8_t obj##_read_init(void)                                                 \
	{                                                                             \
		return nand_lut_read_init(&obj);                                          \
	}                                                                             \
	uint8_t obj##_update_manager(void)                                            \
	{                                                                             \
		return nand_lut_update_manager(&obj);                                     \
	}                                                                             \
	void obj##rtc_update(uint32_t ref_minute)                                     \
	{                                                                             \
		obj.ref_minute = ref_minute;                                              \
	}                                                                             \
	void obj##update_once(void)                                                   \
	{                                                                             \
		obj.update.b_scan_req = 1;                                                \
		obj.update.b_new_req = 1;                                                 \
		obj.update.minute_cnt = 0;                                                \
	}                                                                             \
	const nand_lut_api_type obj##_api = {                                         \
		obj##_open,                                                               \
		obj##_close,                                                              \
		obj##_lut_creat,                                                          \
		obj##_block_erase,                                                        \
		obj##_block_write,                                                        \
		obj##_page_read,                                                          \
		obj##_inp_read,                                                           \
		obj##_addr_read,                                                          \
		obj##_read_init,                                                          \
		obj##_update_manager,                                                     \
		obj##rtc_update,                                                          \
		obj##update_once,                                                         \
	}

#define nand_lut_api_link(obj)                                   \
	{                                                            \
		memcpy(&obj.api, &obj##_api, sizeof(nand_lut_api_type)); \
	}

#endif

/*****************************************************************************
* @brief   END
*****************************************************************************/
